/**
 * 
 */
package qy.jalgotrade.dataseries;

import java.time.ZonedDateTime;
import java.util.HashMap;
import java.util.Map;

import qy.jalgotrade.bar.Bar;

/**
 * 
 * A DataSeries of :class:`pyalgotrade.bar.Bar` instances.
 * 
 * @author c-geo
 *
 */
public class BarDataSeries extends SequenceDataSeries<Bar> {

	private SequenceDataSeries<Double> __openDS;

	private SequenceDataSeries<Double> __closeDS;

	private SequenceDataSeries<Double> __highDS;

	private SequenceDataSeries<Double> __lowDS;

	private SequenceDataSeries<Double> __volumeDS;

	private SequenceDataSeries<Double> __adjCloseDS;

	private Map<String, SequenceDataSeries<Double>> __extraDS;

	private boolean __useAdjustedValues;

	/**
	 * 
	 * 
	 * @throws Exception
	 */
	public BarDataSeries() throws Exception {

		this(0);
	}

	/**
	 * 
	 * 
	 * @param maxLen
	 *            The maximum number of values to hold. Once a bounded length is full, when new
	 *            items are added, a corresponding number of items are discarded from the opposite
	 *            end. If None then dataseries.DEFAULT_MAX_LEN is used.
	 * @throws Exception
	 */
	public BarDataSeries(int maxLen) throws Exception {

		super(maxLen);

		__openDS = new SequenceDataSeries<>(maxLen, Double.class);
		__closeDS = new SequenceDataSeries<>(maxLen, Double.class);
		__highDS = new SequenceDataSeries<>(maxLen, Double.class);
		__lowDS = new SequenceDataSeries<>(maxLen, Double.class);
		__volumeDS = new SequenceDataSeries<>(maxLen, Double.class);
		__adjCloseDS = new SequenceDataSeries<>(maxLen, Double.class);
		__extraDS = new HashMap<>();
		__useAdjustedValues = false;
	}

	private DataSeries<Double> __getOrCreateExtraDS(String name) throws Exception {

		SequenceDataSeries<Double> ret = __extraDS.get(name);
		if (ret == null) {
			ret = new SequenceDataSeries<>(getMaxLen(), Double.class);
			__extraDS.put(name, ret);
		}
		return ret;
	}

	public void setUseAdjustedValues(boolean useAdjusted) {

		__useAdjustedValues = useAdjusted;
	}

	@Override
	public void append(Bar bar) throws Exception {

		appendWithDateTime(bar.getDateTime(), bar);
	}

	@Override
	public void appendWithDateTime(ZonedDateTime dateTime, Bar bar) throws Exception {

		assert dateTime != null;
		assert bar != null;

		bar.setUseAdjustedValue(__useAdjustedValues);
		super.appendWithDateTime(dateTime, bar);

		__openDS.appendWithDateTime(dateTime, bar.getOpen());
		__closeDS.appendWithDateTime(dateTime, bar.getClose());
		__highDS.appendWithDateTime(dateTime, bar.getHigh());
		__lowDS.appendWithDateTime(dateTime, bar.getLow());
		__volumeDS.appendWithDateTime(dateTime, bar.getVolume());
		__adjCloseDS.appendWithDateTime(dateTime, bar.getAdjClose());

		// Process extra columns.
		for (Map.Entry<String, Double> e : bar.getExtraColumns().entrySet()) {
			String name = e.getKey();
			Double value = e.getValue();
			DataSeries<Double> extraDS = __getOrCreateExtraDS(name);
			extraDS.appendWithDateTime(dateTime, value);
		}
	}

	/**
	 * Returns a :class:`pyalgotrade.dataseries.DataSeries` with the open prices.
	 * 
	 * @return
	 */
	public DataSeries<Double> getOpenDataSeries() {

		return __openDS;
	}

	/**
	 * Returns a :class:`pyalgotrade.dataseries.DataSeries` with the close prices.
	 * 
	 * @return
	 */
	public DataSeries<Double> getCloseDataSeries() {

		return __closeDS;
	}

	/**
	 * Returns a :class:`pyalgotrade.dataseries.DataSeries` with the high prices.
	 * 
	 * @return
	 */
	public DataSeries<Double> getHighDataSeries() {

		return __highDS;
	}

	/**
	 * Returns a :class:`pyalgotrade.dataseries.DataSeries` with the low prices.
	 * 
	 * @return
	 */
	public DataSeries<Double> getLowDataSeries() {

		return __lowDS;
	}

	/**
	 * Returns a :class:`pyalgotrade.dataseries.DataSeries` with the volume.
	 * 
	 * @return
	 */
	public DataSeries<Double> getVolumeDataSeries() {

		return __volumeDS;
	}

	/**
	 * Returns a :class:`pyalgotrade.dataseries.DataSeries` with the adjusted close prices.
	 * 
	 * @return
	 */
	public DataSeries<Double> getAdjCloseDataSeries() {

		return __adjCloseDS;
	}

	/**
	 * Returns a :class:`pyalgotrade.dataseries.DataSeries` with the close or adjusted close prices.
	 * 
	 * @return
	 */
	public DataSeries<Double> getPriceDataSeries() {

		if (__useAdjustedValues) {
			return __adjCloseDS;
		} else {
			return __closeDS;
		}
	}

	/**
	 * Returns a :class:`pyalgotrade.dataseries.DataSeries` for an extra column.
	 * 
	 * @return
	 * @throws Exception
	 */
	public DataSeries<Double> getExtraDataSeries(String name) throws Exception {

		return __getOrCreateExtraDS(name);
	}
}
